#include <stdint.h>
#include <functional>
#include <vector>
#include <memory>
#include <unordered_map>
#include <iostream>
#include <unistd.h>

#define TIME_WHEEL_CAPACITI 60

class TimeTask;
using task_fun_t = std::function<void()>;
using TimeTaskPtr = std::shared_ptr<TimeTask>;
using TimeTaskWeakPtr = std::weak_ptr<TimeTask>;
using wheel_func_t = std::function<void()>;

// 供TimeWheel使用的类
class TimeTask
{
public:
    TimeTask(uint64_t id, uint32_t timeout, task_fun_t task_func, wheel_func_t wheel_delete_func)
        : _id(id), _timeout(timeout), _task_func(task_func), _wheel_delete_func(wheel_delete_func),_is_cancel(false)
    {
    }
    ~TimeTask()
    {
        if(_is_cancel == false)
            _task_func();         // 对象销毁时自动执行任务
        _wheel_delete_func(); // 对象销毁时也要把自己从哈希表中删除
    }
    uint64_t Id() { return _id; };
    uint32_t TimeOut() { return _timeout; };
    void Cancel()
    { _is_cancel = true; };
private:
    uint64_t _id;                    // 标识超时任务的唯一性
    uint32_t _timeout;               // 记录任务的超时时间
    task_fun_t _task_func;           // 记录执行的具体任务,由外部绑定传入
    wheel_func_t _wheel_delete_func; // 从时间轮的哈希表中删除，由TimeWheel传入
    bool _is_cancel;
};

// 提供给外部的类
class TimeWheel
{
public:
    TimeWheel()
        : _capacity(TIME_WHEEL_CAPACITI), _tick(0), _timewheel(_capacity)
    {
    }
    void DeleteTimetask(uint64_t id)
    {
        auto it = _all_timetask.find(id);
        if (it != _all_timetask.end())
        {
            _all_timetask.erase(it);
        }
    }
    void AddTimeTask(uint64_t id, uint32_t timeout, task_fun_t task_func)
    {
        // TimeTaskPtr _new_timetask = std::shared_ptr<TimeTask>(new TimeTask(id,timeout,task_func));
        // 根据参数创建任务对象，设置未来从_all_timetask删除自己的回调
        TimeTaskPtr _new_timetask_ptr = std::make_shared<TimeTask>(id, timeout, task_func, std::bind(&TimeWheel::DeleteTimetask, this, id));

        // 存入_all_timetask
        TimeTaskWeakPtr _timetask_weak_ptr = _new_timetask_ptr;
        _all_timetask.emplace(std::make_pair(_new_timetask_ptr->Id(), _timetask_weak_ptr));

        // 把任务放入_timewheel
        uint32_t pos = (_tick + _new_timetask_ptr->TimeOut()) % _capacity;
        _timewheel[pos].push_back(_new_timetask_ptr);
    }
    void RefreshTimeTask(uint64_t id) // 外部提供id，自动刷新对应id的任务
    {
        auto it = _all_timetask.find(id);
        if (it == _all_timetask.end())
        {
            return; // 没找到任务
        }
        // 刷新任务，拿到它的weak_ptr -> shared_ptr
        TimeTaskPtr timetask_ptr = it->second.lock(); // weak_ptr -> shared_ptr
        // 拿到后插入_timewheel
        uint32_t pos = (_tick + timetask_ptr->TimeOut()) % _capacity;
        _timewheel[pos].push_back(timetask_ptr);
    }

    void CancelTimeTask(uint64_t id){
        auto it = _all_timetask.find(id);
        if (it == _all_timetask.end())
        {
            return; // 没找到任务
        }
        TimeTaskPtr task_ptr = it->second.lock();
        task_ptr->Cancel();  
    }
    // 每秒执行一次
    void RunTimeTask()
    {
        // 执行数组中所有任务
        _timewheel[_tick].clear();
        // 移动秒针
        _tick = (_tick + 1) % _capacity;
    }

private:
    uint32_t _capacity;                                          // 记录时间轮容量
    std::vector<std::vector<TimeTaskPtr>> _timewheel;            // 记录时间轮数组
    std::unordered_map<uint64_t, TimeTaskWeakPtr> _all_timetask; // 记录当前所有的任务对象（用于未来刷新Ptr）
    uint32_t _tick;                                              // 秒针的指向
};

// 测试1 创建一个类，定时销毁
class Test
{
public:
    Test()
    {
        std::cout << "构造 Test" << std::endl;
    };
    ~Test()
    {
        std::cout << "析构 Test" << std::endl;
    }
};

void Delete(Test *pt)
{
    delete pt;
}

int main()
{
    Test *pt = new Test();
    TimeWheel time_wheel;
    time_wheel.AddTimeTask(666, 5, std::bind(Delete, pt));

    for (int i = 0; i < 3; i++)
    {
        std::cout << "刷新了任务，将5S后执行" << std::endl;
        time_wheel.RefreshTimeTask(666);
        time_wheel.RunTimeTask();
        sleep(1);
    }

    time_wheel.CancelTimeTask(666);

    while (true)
    {
        std::cout << "1S..." << std::endl;
        time_wheel.RunTimeTask();
        sleep(1);
    }
    return 0;
}